/*
 * Author:
 * 		Davi Costa Clemente 		RA 083413
 * 		Fernando Lener de Arruda	RA 081391
 */

#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>
#include "unistd.h"
#include "common.h"

void showSingleStatistic(SingleStatistics * statistics) {
	int i;
	int dp=0,mean=statistics->totalRequests? (statistics->totalTime / statistics->totalRequests):0;

	printf(" Execuções: %d vezes\n Número de falhas: %d\n Tempo Total: %d us\n Tempo médio: %d us\n",
			statistics->totalRequests,statistics->totalFailures, statistics->totalTime,mean);

	for(i = 0; i < statistics->totalRequests; i++) {
		dp+=(statistics->each[i]-mean)*(statistics->each[i]-mean);
	}

	if((statistics->totalRequests-statistics->totalFailures)>1){
		dp=sqrt(dp/(statistics->totalRequests-statistics->totalFailures-1));
		printf("Desvio padrão: %d us\n",dp);
	}else{
		printf("Desvio padrão não calculado por falta de dados\n");
	}

	printf(" Tempos:\n");
	for(i = 0; i < statistics->totalRequests; i++) {
		printf(" %d",statistics->each[i]);
	}
	printf("\n\n");
}

void showStatistics(Statistics *statistics) {
	printf("Estatísticas de transferência:\n\n");
	printf("Requisições de ISBN e Titulos:\n");
	showSingleStatistic(statistics->times + ALL_ISBNAndTitle);

	printf("Requisições de descrição de livro\n");
	showSingleStatistic(statistics->times + SINGLE_GetDescription);

	printf("Requisições de todas informações do livro\n");
	showSingleStatistic(statistics->times + SINGLE_GetFullInfo);

	printf("Requisições de todas informações de todos os livros\n");
	showSingleStatistic(statistics->times + ALL_GetFullInfo);

	printf("Requisições de atualização do número de exemplares no estoque\n");
	showSingleStatistic(statistics->times + SINGLE_UpdateStock);

	printf("Requisições do número de exemplares no estoque\n");
	showSingleStatistic(statistics->times + SINGLE_GetSampleCount);

}

int sendRequestAndTreatReply(ClientType client, RequestType request,
		Statistics * statistics, int socket,struct sockaddr_in * server_addr) {
	struct sockaddr_in receive_addr;
	struct timeval before,after;
	char *reply; // Resposta pega do servidor
	char * msg; // Msg enviada ao servidor
	char isbn[100];
	int update,processingTime;

	switch (request) {
	case SINGLE_GetDescription:
	case SINGLE_GetFullInfo:
	case SINGLE_UpdateStock:
	case SINGLE_GetSampleCount:
		printf("Digite o ISBN:\n");
		while ('\n' == *fgets(isbn, sizeof(isbn), stdin));
		break;
	case ALL_ISBNAndTitle:
	case ALL_GetFullInfo:
	case CloseConnection: // Enviado para fechar a conexao no final de tudo
		break;
	default:
		break;
	}

	// Tamanho do modificador update stock
	if (request == SINGLE_UpdateStock) {
		printf("Digite o número de livros que deseja escrever:\n");
		while (1 != scanf("%d", &update)) {
			printf("Digite um número válido\n");
			fflush(stdin);
		}
	}

	msg = createBookRequest(request, client, isbn, update);

	gettimeofday(&before,0);

	if (safeSend(msg, socket,server_addr) < 0) {
		statistics->times[request].totalFailures++;
		printf("O envio da mensagem falhou.\n");
		return -1;
	}
	if (safeReceive(&reply, socket,&receive_addr) < 0) {
		statistics->times[request].totalFailures++;
		printf("Não foi recebida nenhuma mensagem do servidor\n");
		return -1;
	}

	gettimeofday(&after,0);

	readInt(reply,&processingTime);

	if(request < CloseConnection){
		int total=1000000*(after.tv_sec-before.tv_sec)+(after.tv_usec-before.tv_usec);
		int requestsNum=statistics->times[request].totalRequests++;

		statistics->times[request].totalTime+=(total-processingTime)/2;
		statistics->times[request].each[requestsNum]=(total-processingTime)/2;
	}
	printf("%s", reply+4);

	free(reply);

	return 0;
}


int main(int argc, char **argv) {
	struct sockaddr_in server_addr;
	ClientType client;
	RequestType request;
	Statistics statistics;
	int option = 0;
	int fd = 0;

	if(argc<3 || atoi(argv[2])<10000){
		printf("Uso: <endereço> <porta>\n");
		exit(1);
	}else if ((fd=pseudoUDPConnect(argv[1], atoi(argv[2]),&server_addr))<=0) {
		printf("Não foi possível fazer a associação do socket ao IP e porta do servidor\n");
		exit(1);
	}

	bzero((void*) &statistics, sizeof(Statistics));

	printf("Qual seu usuário?\n");

	do {
		printf("1-Usuário Comum\n");
		printf("2-Livraria\n");

		if (1 != scanf("%d", &option)) {
			fflush(stdin);
			option = -1;
		}

		switch (option) {
		case 1:
			client = User;
			break;
		case 2:
			client = Bookstore;
			break;
		default:
			printf("Por favor, digite 0 ou 1\n");
			continue;
			break;
		}

	} while (0);



	do {
		printf("Que operação deseja executar?\n");

		printf("1-Listar todos os títulos e ISBN\n");
		printf("2-Mostrar descrição do livro\n");
		printf("3-Mostrar toda informação do livro\n");
		printf("4-Mostrar toda informação de todos os livros\n");
		printf("5-Atualizar o número de exemplares no estoque\n");
		printf("6-Mostrar o número de exemplares no estoque\n");
		printf("7-Mostrar estatisticas\n");
		printf("8-Finalizar programa\n");

		if (1 != scanf("%d", &option)) {
			fflush(stdin);
			option = -1;
		}

		switch (option) {
		case 1:
			request = ALL_ISBNAndTitle;
			break;
		case 2:
			request = SINGLE_GetDescription;
			break;
		case 3:
			request = SINGLE_GetFullInfo;
			break;
		case 4:
			request = ALL_GetFullInfo;
			break;
		case 5:
			request = SINGLE_UpdateStock;
			break;
		case 6:
			request = SINGLE_GetSampleCount;
			break;
		case 7:
			showStatistics(&statistics);
			continue;
			break;
		case 8:
			printf("Fechando a conexão...\n");
			request = CloseConnection;
			break;
		default:
			printf("Digite uma opção valida");
			break;
		}

		sendRequestAndTreatReply(client, request, &statistics, fd,&server_addr);
	} while (request!=CloseConnection);

	return 0;
}
